/**
 * Copyright 2011 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.api.adwords.starterkit.wrappers;

import com.google.api.adwords.starterkit.db.keys.AdGroupKey;
import com.google.api.adwords.starterkit.db.keys.KeywordKey;
import com.google.api.adwords.starterkit.db.keys.TextAdKey;
import com.google.api.adwords.starterkit.db.structures.AdGroupStructure;
import com.google.api.adwords.starterkit.db.tables.AdGroupTable;
import com.google.api.adwords.starterkit.settings.AdGroupSettings;
import com.google.api.adwords.starterkit.settings.KeywordSettings;
import com.google.api.adwords.starterkit.settings.TextAdSettings;
import com.google.api.adwords.starterkit.utils.AdTemplate;
import com.google.api.adwords.v201101.cm.AdGroupStatus;

import java.sql.Connection;
import java.util.HashMap;

/**
 * The Class AdGroupWrapper manages all AdGroups synchronization process with
 * the starterkit DB.
 * 
 * @author rodrigo.f.vale@gmail.com (Rodrigo Vale)
 */
public class AdGroupWrapper {
  private AdGroupSettings adGroupSettings;
  private Connection conn = null;
  private AdGroupStructure adGroupStructure;
  private AdGroupTable adGroupTable;

  private HashMap<KeywordKey, KeywordWrapper> keywordWrappers =
      new HashMap<KeywordKey, KeywordWrapper>();

  private HashMap<TextAdKey, TextAdWrapper> textAdWrappers =
      new HashMap<TextAdKey, TextAdWrapper>();

  /**
   * Instantiates a new ad group wrapper. This is the standard constructor. It
   * will automatically generate a new AdGroup on DB tied to campaignWrapper. It
   * will also sync new settings with old settings from prior executions.
   * 
   * @param campaignWrapper the campaign wrapper
   * @param adGroupName the ad group name
   * @param adGroupSettings the starter kit settings
   * @param conn the DB connection
   */
  public AdGroupWrapper(final CampaignWrapper campaignWrapper,
      final String adGroupName, final AdGroupSettings adGroupSettings,
      Connection conn) {
    this.conn = conn;
    this.adGroupSettings = adGroupSettings;
    adGroupStructure = new AdGroupStructure();

    adGroupStructure.setName(adGroupName);
    adGroupStructure.setStatus(adGroupSettings.getStatus());
    adGroupStructure.setMaxCpc(adGroupSettings.getCpc());
    adGroupStructure.setMaxCpm(adGroupSettings.getCpm());
    adGroupStructure.setCampaignId(campaignWrapper.getCampaignId());
    adGroupStructure.setLocalCampaignId(campaignWrapper.getLocalId());

    adGroupStructure.setDefaultCpcModificable(true);
    adGroupStructure.setStatusModifiable(true);

    adGroupTable = new AdGroupTable(conn);

    AdGroupKey adGroupKey =
        new AdGroupKey(adGroupStructure.getLocalCampaignId(), 
            adGroupStructure.getName());
    
    // the synchronization process
    if (adGroupTable.exists(adGroupKey)) {
      synchronize(adGroupKey);
      adGroupTable.setTouched(adGroupKey, true);
    } else {
      adGroupStructure = adGroupTable.save(adGroupStructure, true, true);
    }
  }

  public String getName() {
    return adGroupStructure.getName();
  }

  public int getLocalId() {
    return adGroupStructure.getLocalId();
  }

  public void setLocalId(int localId) {
    adGroupStructure.setLocalId(localId);
    adGroupTable.save(adGroupStructure, true, true);
  }

  public long getCampaignId() {
    return adGroupStructure.getCampaignId();
  }

  public void setCampaignId(long campaignId) {
    adGroupStructure.setCampaignId(campaignId);
  }

  public long getAdGroupId() {
    return adGroupStructure.getAdGroupId();
  }

  public void setAdGroupId(int adGroupId) {
    adGroupStructure.setAdGroupId(adGroupId);
    adGroupTable.save(adGroupStructure, true, true);
  }

  /**
   * Gets the flag related to the ability of the framework to modify the AdGroup
   * status on the AdWords account. If the AdGroup status is modified from
   * AdWords interface (by a human), the framework assumes that it can't touch
   * on the status anymore.
   * See the canModifyXXXFlags at the manual for a deep dive explanation.
   * 
   * @return true if the frameworks can modify the status automatically
   */
  public boolean getCanModifyStatus() {
    return adGroupStructure.isStatusModifiable();
  }

  /**
   * Sets the can modify status flag. If set to false, StarterKit will not
   * modify the AdGroup Status even when direct requested by application.
   * See the canModifyXXXFlags at the manual for a deep dive explanation.
   * 
   * @param canModifyStatus the new can modify status
   */
  public void setCanModifyStatus(boolean canModifyStatus) {
    adGroupStructure.setStatusModifiable(canModifyStatus);
    adGroupTable.save(adGroupStructure, true, true);
  }

  public boolean getCanModifyDefaultCpc() {
    return adGroupStructure.isDefaultCpcModificable();
  }

  /**
   * Sets the can modify default cpc flag. If set to false, StarterKit will not
   * modify the MaxCPC of an AdGroup even when direct requested by application.
   * See the canModifyXXXFlags at the manual for a deep dive explanation. 
   * 
   * @param canModifyDefaultCpc the new can modify default cpc
   */
  public void setCanModifyDefaultCpc(boolean canModifyDefaultCpc) {
    adGroupStructure.setDefaultCpcModificable(canModifyDefaultCpc);
    adGroupTable.save(adGroupStructure, true, true);
  }

  public long getMaxCpc() {
    return adGroupStructure.getMaxCpc();
  }

  public void setMaxCpc(long maxCpc) {
    adGroupStructure.setMaxCpc(maxCpc);
    adGroupTable.save(adGroupStructure, true, true);
  }

  public void setStatus(AdGroupStatus status) {
    adGroupStructure.setStatus(status);
    adGroupTable.save(adGroupStructure, true, true);
  }

  public AdGroupStatus getStatus() {
    return adGroupStructure.getStatus();
  }

  /**
   * Synchronize the AdGroup settings just created from the system with legacy
   * AdGroup settings
   * 
   * @param adGroupKey the AdGroup key to load content from DB
   */
  private void synchronize(AdGroupKey adGroupKey) {
    AdGroupStructure adGroupDB = adGroupTable.get(adGroupKey);

    adGroupStructure.setAdGroupId(adGroupDB.getAdGroupId());
    adGroupStructure.setLocalCampaignId(adGroupDB.getLocalCampaignId());
    adGroupStructure.setLocalId(adGroupDB.getLocalId());
    adGroupStructure.setCampaignId(adGroupDB.getCampaignId());
    adGroupStructure.setBanned(adGroupDB.isBanned());

    adGroupStructure.setDefaultCpcModificable(adGroupDB.isDefaultCpcModificable());
    adGroupStructure.setStatusModifiable(adGroupDB.isStatusModifiable());

    boolean modified = false;

    if (adGroupSettings.isBidModifiable()) {
      if (adGroupStructure.getMaxCpc() != adGroupDB.getMaxCpc()) {
        if (!adGroupDB.isDefaultCpcModificable()) {
          adGroupStructure.setMaxCpc(adGroupDB.getMaxCpc());
        } else {
          modified = true;
        }
      }
    } else {
      adGroupStructure.setMaxCpc(adGroupDB.getMaxCpc());
    }

    if (adGroupSettings.isStatusModifiable()) {
      if (!adGroupStructure.getStatus().equals(adGroupDB.getStatus())) {
        if (!adGroupDB.isStatusModifiable()) {
          adGroupStructure.setStatus(adGroupDB.getStatus());
        } else {
          modified = true;
        }
      }
    } else {
      adGroupStructure.setStatus(adGroupDB.getStatus());
    }

    if (modified) {
      adGroupTable.save(adGroupStructure, true, true);
    }
  }

  /**
   * Gets the keyword wrapper.
   * 
   * @param keyword the keyword
   * @param negative the negative
   * @param destUrl the dest url
   * @return the keyword wrapper
   */
  public KeywordWrapper getKeywordWrapper(String keyword, boolean negative,
      String destUrl, KeywordSettings keywordSettings) {
    KeywordKey keywordKey =
        new KeywordKey(adGroupStructure.getLocalId(), keyword,
            keywordSettings.getMatchType());
    if (keywordWrappers.containsKey(keywordKey)) {
      return keywordWrappers.get(keywordKey);
    } else {
      KeywordWrapper keywordWrapper =
          new KeywordWrapper(this, keyword, negative, destUrl, keywordSettings,
              conn);
      keywordWrappers.put(keywordKey, keywordWrapper);
      return keywordWrapper;
    }
  }

  /**
   * Gets the adText wrapper based on a Ad Template.
   * 
   * @param adTemplate the Ad Template
   * @return the text wrapper
   */
  public TextAdWrapper getTextWrapper(AdTemplate adTemplate,
      TextAdSettings textAdSettings) {
    return getTextWrapper(adTemplate.getHead(), adTemplate.getLine1(),
        adTemplate.getLine2(), adTemplate.getDestUrl(), textAdSettings);
  }

  /**
   * Gets the adText wrapper based on an Text Ads fields.
   * 
   * @param headline the textAd headline
   * @param desc1 the textAd desc1
   * @param desc2 the textAd desc2
   * @param destinationUrl the textAd destination url
   * @return the adText wrapper
   */
  public TextAdWrapper getTextWrapper(String headline, String desc1,
      String desc2, String destinationUrl, TextAdSettings textAdSettings) {
    TextAdKey textAdKey =
        new TextAdKey(adGroupStructure.getLocalId(), headline, desc1, desc2);
    if (textAdWrappers.containsKey(textAdKey)) {
      return textAdWrappers.get(textAdKey);
    } else {
      TextAdWrapper textAdWrapper =
          new TextAdWrapper(this, headline, desc1, desc2, destinationUrl,
              textAdSettings, conn);
      textAdWrappers.put(textAdKey, textAdWrapper);
      return textAdWrapper;
    }
  }

  public void setMaxCpa(long maxCpa) {
    adGroupStructure.setMaxCpa(maxCpa);
    adGroupTable.save(adGroupStructure, true, true);
  }

  public void setMaxCpm(long maxCpm) {
    adGroupStructure.setMaxCpm(maxCpm);
    adGroupTable.save(adGroupStructure, true, true);
  }

  public long getMaxCpa() {
    return adGroupStructure.getMaxCpa();
  }

  public long getMaxCpm() {
    return adGroupStructure.getMaxCpm();
  }
}